package src.model.gameObject;

import src.model.gameObject.ability.Ability;
import src.model.gameObject.ability.AbilityReturnResult;
import src.model.gameObject.ability.AbilityType;
import src.model.gameObject.ability.ActiveAbility;
import src.model.gameWorld.Player;
import src.model.gameWorld.PlayerLookupTable;
import src.model.gameWorld.Position;
import java.util.Queue;
import java.util.LinkedList;

import java.util.Vector;

/**
 * <b>Overview</b><br>
 * <p>PlayerGAmeObjects are GameObjects that are owned by players. They can be given commands from the player.
 * They have extra Abilities, called ActiveAbilities, that only PGO's can hold and use.</p>
 * <br><br>
 * <b>Responsibilites</b><br>
 * <p>Encapsulate player owned objects.</p>
 * <br><br>
 * <b>Collaborators</b>
 * <br>
 * ActiveAbility - The PGO's active abilities.
 * Player - The player that owns it.
 * <br>
 * <b>Implementor</b> - <br>
 * <b>Tester</b> - <br>
 * @author Tristan
 *
 */
public class PlayerGameObject extends GameObject {
	
	private Vector<ActiveAbility> activeAbilities;
	private Queue<EnqueuePair> abilityQueue;
	private Player owner;
	
	/**
	 * Creates a new PlayerGameObject
	 * @param type
	 * @param subType
	 * @param owner
	 */
	public PlayerGameObject(GameObjectType type, GameObjectSubtype subType, Player owner) {
		super(type, subType);
		activeAbilities = new Vector<ActiveAbility>();
		abilityQueue = new LinkedList<EnqueuePair>();
		this.owner = owner;
	}

	/**
	 * Adds an instance of ActiveAbility to its ActiveAbility collection
	 * @param newActiveAbil
	 */
	public void addActiveAbility(ActiveAbility newActiveAbil){
		activeAbilities.add(newActiveAbil);
	}
	
	/**
	 * Removes a specific instance of ActiveAbility from the AciveAbility collecion
	 * @param aa
	 */
	public void removeActiveAbility(ActiveAbility aa){
		activeAbilities.remove(aa);
	}
	
	/**
	 * Adds a collection of ActiveAbilities to the ActiveAbility collection
	 * @param newActiveAbils
	 */
	public void addAllActiveAbility(Vector<ActiveAbility> newActiveAbils){
		activeAbilities.addAll(newActiveAbils);
	}
	
	public Vector<Ability> getAbilities(){
		Vector<Ability> abilities = super.getAbilities();
		abilities.addAll(activeAbilities);
		return abilities;
	}
	
	public Ability getAbility(AbilityType abilType){
		Ability abil = getActiveAbility(abilType);
		if(abil != null)
			return abil;

		Vector<Ability> passives = super.getAbilities();
		for(Ability passive : passives){
			if(passive.abilityType() == abilType)
				return passive;
		}

		return null;	
	}
	
	/**
	 * Gets a specific ActiveAbility
	 * @param abilType
	 * @return The first instance of the desired ability.
	 */
	public ActiveAbility getActiveAbility(AbilityType abilType){
		for(ActiveAbility abil : activeAbilities){
			if(abil.abilityType() == abilType)
				return abil;
		}

		return null;
	}

	/**
	 * 
	 * @return All of the active abilities.
	 */
	public Vector<AbilityType> getActiveAbilityTypeList(){
		Vector<AbilityType> abilTypeList = new Vector<AbilityType>();

		for(ActiveAbility abil : activeAbilities){
			abilTypeList.add(abil.abilityType());
		}
	
		return abilTypeList;
	}
	
	/**
	 * 
	 * @return
	 */
	public Player player(){ return owner; }

	/**
	 * 
	 * @param abilType
	 * @return Whether or not it has the specified ability type.
	 */
	public boolean hasAbility(AbilityType abilType){
		for(ActiveAbility abil : activeAbilities){
			if(abil.abilityType() == abilType)
				return true;
		}
		for(Ability passiveAbil : getAbilities()){
			if(passiveAbil.abilityType() == abilType)
				return true;
		}
		return false;
	}	
	
	/**
	 * Adds an ability to the ability exucution queue (missions it) along with the desired parameters. If this is the
	 * only thing in the queue, it will execute it.
	 * @param aa
	 * @param position
	 */
	public void enqueueAbility(ActiveAbility aa, Position...position){
		abilityQueue.offer(new EnqueuePair(aa, position));
		if (abilityQueue.size() >= 1) runQueue();
	}
	
	/**
	 * clears the ability queue.
	 *
	 */
	public void clearAbilityQueue(){
		abilityQueue.clear();
	}
	
	/**
	 * Executes things at the front of the queue until they stop being successful. A successfully
	 * executing ability is then removed from the queue.
	 *
	 */
	private void runQueue(){
		AbilityReturnResult arr = AbilityReturnResult.SUCCESS;
		while (true){
			if (abilityQueue.size() == 0) break;
			EnqueuePair runIt = abilityQueue.peek();
			arr = runIt.active().execute(runIt.position());
			if (arr == AbilityReturnResult.FAILIED) break;
			if (arr == AbilityReturnResult.IMPOSSIBLE){
				clearAbilityQueue();
				break;
			}
			abilityQueue.poll();
		}
	}
	
	/**
	 * Does all of the new turn stuff.
	 *
	 */
	public void newTurn(){
		// TODO: Make it listen for new turns
		runQueue();
	}
	
	public PlayerGameObject clone(){
		PlayerGameObject cloner =  new PlayerGameObject(super.getType(), super.getSubtype(), owner);
		cloner.addAllActiveAbility(activeAbilities);
		return cloner;
	}
	
	
	private class EnqueuePair{
		ActiveAbility aa;
		Position[] position;
		
		public EnqueuePair(ActiveAbility aa, Position...position){
			this.aa = aa;
			this.position = position;
		}
		
		public ActiveAbility active(){return aa;}
		public Position[] position(){return position;}
	}
	
	public void resetAbilites()
	{
		for(ActiveAbility aa : activeAbilities)
			aa.newTurn();
	}
}


